/* 第一步：手写一个call方法 */
// 思路：将~要改变this指向的方法，挂到目标对象上执行并返回
Function.prototype.mycall = function(context) {
    if(typeof this !== 'function') {
        throw new TypeError('not function')
    }
    context = context || window
    //***************************************************************************************************
    // 这三步也可以这样理解：
    // this指的就是调用call 方法的函数
    // 给当前上下文(call方法的第一个参数)添加一个属性fn，属性值为this，当前上下文就可以调用这个方法了。
    // 把call方法传入的其他的参数传入到属性fn指代的函数中，直接调用函数
    // f.myCall(obj) 就是将f方法添加到obj的属性中，f中的this 指向 obj

    context.fn = this;                   //this指向的是当前的函数（Function实例）
    let arg = [...arguments].slice(1);   //获取除了this指向对象以外的参数，空数组slice后返回仍是空数组
    let result = context.fn(...arg);     //隐式绑定，当前函数的this指向context
    //***************************************************************************************************
    delete context.fn
    return result
}
let name = 'hello';
let age = 1000;
let my_obj =  {
    name:'ssx',
    my_age:18,
    myFun:function (fm,to) {
        console.log(this.name,this.age,fm,to);
    }
}

let test_obj = {
    name:'lili',
    age:19
}
// my_obj.myFun();
my_obj.myFun.mycall(test_obj,'fujian','shenzheng');

// my_obj.mycall()

/* 第二步：手写一个apply方法 */
Function.prototype.myApply = function(context) {
    if(typeof this !== 'function') {
        throw new TypeError('not function')
    }
    context = context || window
    //***************************************************************************************************
    // 这三步也可以这样理解：
    // this指的就是调用call 方法的函数
    // 给当前上下文(call方法的第一个参数)添加一个属性fn，属性值为this，当前上下文就可以调用这个方法了。
    // 把call方法传入的其他的参数传入到属性fn指代的函数中，直接调用函数
    // f.myCall(obj) 就是将f方法添加到obj的属性中，f中的this 指向 obj

    context.fn = this;                   //this指向的是当前的函数（Function实例）
    let arg = [...arguments].slice(1);   //获取除了this指向对象以外的参数，空数组slice后返回仍是空数组
    let result = context.fn(...arg[0]);     //隐式绑定，当前函数的this指向context
    //***************************************************************************************************
    delete context.fn
    return result
}

my_obj.myFun.myApply(test_obj,['fujian','shenzheng']);

/* 第三步：手写一个bind方法:关键点是闭包 */
Function.prototype.myBind = function(context) {
    if(typeof this !== 'function') {
        throw new Error('not a function!!');
    }
    let self = this;
    let args = [ ...arguments ].slice(1);
    let bound = function() {
        self.apply(context, [...args])
    }
    return bound
}

my_obj.myFun.myBind(test_obj,'fujian','shenzheng')();　
